﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Web;
using System.Diagnostics;
using TicketSellingServer;
using System.Threading;

namespace FlightSearchServer
{
    public class FlightSearchServiceClients : IFlightSearchServiceClients
    {

        public TicketList SearchTickets(string date, string source, string destination)
        {
            //checking all parameters are present and non-empty
            if (string.IsNullOrEmpty(date) || string.IsNullOrEmpty(source) || string.IsNullOrEmpty(destination))
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
                return null;
            }

            SellerFlightsList sellerTickets = new SellerFlightsList();

            //obtaining sellers object lock to prevent deleting/creating of sellers during searching tickets
            ServerData.sellingServersLock.AcquireReaderLock(Timeout.Infinite);

            //asking each selling server for available tickets, and storing them in common list
            foreach (var sellerName in ServerData.sellingServers.Keys)
            {
                try
                {
                    SellerFlightsList currentSellerFlights;
                    using (new OperationContextScope((IContextChannel)ServerData.sellingServers[sellerName].InnerChannel))
                    {
                        currentSellerFlights = ServerData.sellingServers[sellerName].SearchTickets(date, source, destination);
                    }

                    if (currentSellerFlights != null)
                    {
                        sellerTickets.AddRange(currentSellerFlights);
                    }
                }
                catch (EndpointNotFoundException)
                {
                    //pass, looks like one of the server not available, so just waiting till it will be ok
                }
                catch (FaultException)
                {
                    //pass, looks like server has thrown an error, probably it is an search server internal error
                }
            }

            ServerData.sellingServersLock.ReleaseReaderLock();

            //converting received data to clients' format
            TicketList tickets = new TicketList(sellerTickets);
            tickets.Sort();
            WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
            return tickets;
        }

        public string CreateReservation(string seller, string flight, string date)
        {
            //checking all parameters are present and non-empty
            if (string.IsNullOrEmpty(seller) || string.IsNullOrEmpty(flight) || string.IsNullOrEmpty(date))
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
                return null;
            }

            //checking if given seller is one of connected sellers
            if (!ServerData.sellingServers.ContainsKey(seller))
            {
                WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound("Failed, unknown seller");
                return null;
            }
           
            string reservationId = null;
            try
            {
                using (new OperationContextScope((IContextChannel)ServerData.sellingServers[seller].InnerChannel))
                {
                    reservationId = ServerData.sellingServers[seller].ReserveTicket(flight, date);
                }
            }
            catch (FaultException e)
            {
                WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound(e.Reason.ToString());
                return null;
            }
            catch (EndpointNotFoundException)
            {
                WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound(String.Format("Failed, {0} server is down", seller));
                return null;
            }

            if (reservationId == null)
            {
                //server returned an illegal value without throwing error message, looks like it is not ok
                WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound(String.Format("Failed, {0} server is down", seller));
                return null;
            }

            string baseUri = System.ServiceModel.Web.WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri.OriginalString;
            WebOperationContext.Current.OutgoingResponse.SetStatusAsCreated(new Uri(baseUri + "/" + reservationId));
            return reservationId;
        }

        public void CancelReservation(string seller, string id)
        {
            //checking all parameters are present and non-empty
            if (string.IsNullOrEmpty(seller) || string.IsNullOrEmpty(id))
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
                return;
            }

            //checking if given seller is one of connected sellers
            if (!ServerData.sellingServers.ContainsKey(seller))
            {
                WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound("Failed, unknown seller");
                return;
            }

            try
            {
                using (new OperationContextScope((IContextChannel)ServerData.sellingServers[seller].InnerChannel))
                {
                    ServerData.sellingServers[seller].CancelTicket(id);
                }
            }
            catch (FaultException e)
            {
                WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound(e.Reason.ToString());
                return;
            }
            catch (EndpointNotFoundException)
            {
                WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound(String.Format("Failed, {0} server is down", seller));
                return;
            }

            WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
            return;
        }

    }
}
